/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/** *****************************************************************************************************
 * \file     Wes.c
 * \brief    AUTOSAR 4.3.1 MCAL Wes Driver
 *
 * <table>
 * <tr><th>Date           <th>Version
 * <tr><td>2024/12/13     <td>1.0.0
 * </table>
 *******************************************************************************************************/
/********************************************************************************************************
 *                                      Include header files
 *******************************************************************************************************/
#include "Wes.h"
#include "Wes_Regs.h"

/********************************************************************************************************
 *                                  Private Macro Declarations
 *******************************************************************************************************/
#define WES_POLLING_SET_DONE_MAX_TIMES (16000U) /* 10 * 32K clock cycle */

#define WES_TIMER_COM_CTRL(n) (0x100U + (n) * 0x100U)
#define WES_TIMER_EVENT_CTRL0(n, e) (WES_TIMER_COM_CTRL(n) + 0x4U + (e) * 0x8U)
#define WES_TIMER_EVENT_CTRL1(n, e) (WES_TIMER_COM_CTRL(n) + 0x8U + (e) * 0x8U)
#define WES_TIMER_SET_DONE(n) (0x150U + (n) * 0x100U)
#define WES_TIMER_STA(n) (0x154U + (n) * 0x100U)

/********************************************************************************************************
 *                                  Private Type Declarations *
 *******************************************************************************************************/
typedef struct {
    uint32_t reg_data0;
    uint32_t reg_data1;
    uint32_t reg_data2;
} Wes_DigitalDataType;

/********************************************************************************************************
 *                                  Private Function Declarations *
 *******************************************************************************************************/
/**
 * @brief Wait for a specific bit in a register to match the expected value.
 *
 * This function checks the specified bit in a register and waits until it
 * matches the expected value or until the loop counter expires.
 *
 * @param reg       The address of the register to read.
 * @param bitShift  The number of bit positions to shift left to get the desired
 * bit.
 * @param expect    The expected value (0 or 1) of the specified bit.
 * @param loopCnt   The number of iterations to wait before timing out.
 *
 * @return true if the bit matches the expected value before the loop counter
 * expires, false otherwise.
 */
static inline bool Wes_WaitBit(uint32_t reg, uint32_t bitShift, uint32_t expect,
                               uint32_t loopCnt)
{
    volatile uint32_t loop = loopCnt;

    while (
        (loop != 0U) &&
        (expect != ((readl(reg) & ((uint32_t)1U << bitShift)) >> bitShift))) {
        loop--;
    }

    return loop > 0U ? true : false;
}

/**
 * @brief Converts digital sample port configuration to register settings.
 *
 * This function takes a configuration structure containing digital sample port
 * settings and converts these settings into a register data structure that can
 * be used to configure hardware registers.
 *
 * @param cfg Pointer to a structure of type Wes_DigitalSamplePortType
 * containing the digital sample port configuration.
 * @param reg Pointer to a structure of type Wes_DigitalDataType where the
 *            converted register settings will be stored.
 *
 * @return Returns WES_OK if the conversion is successful.
 */
static int32_t Wes_DigitalCfgToReg(Wes_DigitalSamplePortType *cfg,
                                   Wes_DigitalDataType *reg)
{
    reg->reg_data0 =
        FV_WES_REF_DATA0_REF_DATA0(cfg->portData[WES_DIGITAL_SAMPLE_DATA0]);

    reg->reg_data1 =
        FV_WES_REF_DATA1_REF_DATA1(cfg->portData[WES_DIGITAL_SAMPLE_DATA1]) |
        FV_WES_REF_DATA1_REF_DATA2(cfg->portData[WES_DIGITAL_SAMPLE_DATA2]) |
        FV_WES_REF_DATA1_REF_DATA3(cfg->portData[WES_DIGITAL_SAMPLE_DATA3]) |
        FV_WES_REF_DATA1_REF_DATA4(cfg->portData[WES_DIGITAL_SAMPLE_DATA4]);

    reg->reg_data2 =
        FV_WES_REF_DATA2_REF_DATA5(cfg->portData[WES_DIGITAL_SAMPLE_DATA5]) |
        FV_WES_REF_DATA2_REF_DATA6(cfg->portData[WES_DIGITAL_SAMPLE_DATA6]) |
        FV_WES_REF_DATA2_REF_DATA7(cfg->portData[WES_DIGITAL_SAMPLE_DATA7]);

    return WES_OK;
}

/**
 * @brief Converts digital register data to configuration format.
 *
 * This function takes the digital register data as input and converts it to
 * the configuration format for the sample ports.
 *
 * @param reg Pointer to the digital register data type. This should contain
 *            the register data to be converted.
 * @param cfg Pointer to the digital sample port type. This will be filled with
 *            the converted configuration data.
 *
 * @return Returns WES_OK if the conversion is successful.
 */
static int32_t Wes_DigitalRegToCfg(Wes_DigitalDataType *reg,
                                   Wes_DigitalSamplePortType *cfg)
{
    cfg->portData[WES_DIGITAL_SAMPLE_DATA0] =
        GFV_WES_SAMP_DATA0_SAMP_DATA0(reg->reg_data0);

    cfg->portData[WES_DIGITAL_SAMPLE_DATA1] =
        GFV_WES_SAMP_DATA1_SAMP_DATA1(reg->reg_data1);
    cfg->portData[WES_DIGITAL_SAMPLE_DATA2] =
        GFV_WES_SAMP_DATA1_SAMP_DATA2(reg->reg_data1);
    cfg->portData[WES_DIGITAL_SAMPLE_DATA3] =
        GFV_WES_SAMP_DATA1_SAMP_DATA3(reg->reg_data1);
    cfg->portData[WES_DIGITAL_SAMPLE_DATA4] =
        GFV_WES_SAMP_DATA1_SAMP_DATA4(reg->reg_data1);

    cfg->portData[WES_DIGITAL_SAMPLE_DATA5] =
        GFV_WES_SAMP_DATA2_SAMP_DATA5(reg->reg_data2);
    cfg->portData[WES_DIGITAL_SAMPLE_DATA6] =
        GFV_WES_SAMP_DATA2_SAMP_DATA6(reg->reg_data2);
    cfg->portData[WES_DIGITAL_SAMPLE_DATA7] =
        GFV_WES_SAMP_DATA2_SAMP_DATA7(reg->reg_data2);

    return WES_OK;
}

/********************************************************************************************************
 *                                  Global Function Declarations *
 *******************************************************************************************************/
/**
 * @brief Sets the function control parameters for the WES.
 *
 * This function configures various registers of the WES IP based on the input
 * parameters provided in the `funcCtrl` structure. It includes settings for
 * functional clock monitoring, sample modes, trigger selections, work modes,
 * digital and analog enable/disable, reference data, comparison enable,
 * stabilization values, analog control, and analog watchdog timer.
 *
 * @param base The base address of the WES IP registers.
 * @param funcCtrl Pointer to a `Wes_FunctionCtrlType` structure containing the
 *        configuration parameters.
 *
 * @return `WES_OK` on successful configuration, or an error code indicating the
 *         specific failure.
 */
int32_t Wes_SetFunctionCtrl(uint32_t base, Wes_FunctionCtrlType *funcCtrl)
{
    Wes_DigitalDataType digitalData;
    uint32_t regVal;
    int32_t ret;

    /* Config WES_FUNC_CTRL */
    regVal = readl(base + WES_FUNC_CTRL_OFF);

    if (funcCtrl->funcClkMonEn) {
        regVal |= BM_WES_FUNC_CTRL_FUNC_CLK_MON_EN;
    } else {
        regVal &= ~BM_WES_FUNC_CTRL_FUNC_CLK_MON_EN;
    }

    regVal |= BM_WES_FUNC_CTRL_FUNC_CLK_EN;

    regVal &= ~FM_WES_FUNC_CTRL_FUNC_CLK_SEL;
    regVal |= FV_WES_FUNC_CTRL_FUNC_CLK_SEL(funcCtrl->funcClkSel);

    if (funcCtrl->analogSampleMode == WES_SAMPLE_MODE_0) {
        regVal &= ~BM_WES_FUNC_CTRL_ANA_WORK_MODE;
    } else {
        regVal |= BM_WES_FUNC_CTRL_ANA_WORK_MODE;
    }

    if (funcCtrl->digitalSampleMode == WES_SAMPLE_MODE_0) {
        regVal &= ~BM_WES_FUNC_CTRL_DIG_WORK_MODE;
    } else {
        regVal |= BM_WES_FUNC_CTRL_DIG_WORK_MODE;
    }

    regVal &= ~FM_WES_FUNC_CTRL_SAMP_NUM;
    regVal |= FV_WES_FUNC_CTRL_SAMP_NUM(funcCtrl->digitalSampleNum);

    if (funcCtrl->triggerSel == WES_SAMPLE_TRIGGER_FROM_TIMER0) {
        regVal &= ~BM_WES_FUNC_CTRL_TRIGGER_SEL;
    } else {
        regVal |= BM_WES_FUNC_CTRL_TRIGGER_SEL;
    }

    if (funcCtrl->workMode == WES_NORMAL_MODE) {
        regVal &= ~BM_WES_FUNC_CTRL_WES_WORK_MODE;
    } else {
        regVal |= BM_WES_FUNC_CTRL_WES_WORK_MODE;
    }

    regVal &= ~FM_WES_FUNC_CTRL_DSEL_NUM;
    regVal |= FV_WES_FUNC_CTRL_DSEL_NUM(funcCtrl->digitalSelectPort);

    regVal &= ~FM_WES_FUNC_CTRL_ANA_TRG_SEL;
    regVal |= FV_WES_FUNC_CTRL_ANA_TRG_SEL(funcCtrl->analogTriggerIndex);

    regVal &= ~FM_WES_FUNC_CTRL_DIG_TRG_SEL;
    regVal |= FV_WES_FUNC_CTRL_DIG_TRG_SEL(funcCtrl->digitalTriggerIndex);

    if (funcCtrl->analogEnable) {
        regVal |= BM_WES_FUNC_CTRL_ANA_MODE_EN;
    } else {
        regVal &= ~BM_WES_FUNC_CTRL_ANA_MODE_EN;
    }

    if (funcCtrl->digitalEnable) {
        regVal |= BM_WES_FUNC_CTRL_DIG_MODE_EN;
    } else {
        regVal &= ~BM_WES_FUNC_CTRL_DIG_MODE_EN;
    }

    writel(regVal, base + WES_FUNC_CTRL_OFF);
    writel(regVal, base + WES_FUNC_CTRL_1_OFF);

    /* Config WES_SAMPE_DATA2 */
    if (funcCtrl->digitalSampleDataUpt ==
        WES_DIGITAL_SAMPLE_DATA_UPDATE_WHEN_COMPARE_ERROR) {
        regVal = 0U;
    } else {
        regVal = BM_WES_SAMP_DATA2_SAMP_DATA_MODE;
    }

    writel(regVal, base + WES_SAMP_DATA2_OFF);
    writel(regVal, base + WES_SAMP_DATA2_RED_OFF);

    /* Config WES_REF_DATA0~2 */
    Wes_DigitalCfgToReg(&funcCtrl->digitalRefData, &digitalData);
    writel(digitalData.reg_data0, base + WES_REF_DATA0_OFF);
    writel(digitalData.reg_data1, base + WES_REF_DATA1_OFF);
    writel(digitalData.reg_data2, base + WES_REF_DATA2_OFF);

    writel(digitalData.reg_data0, base + WES_REF_DATA0_RED_OFF);
    writel(digitalData.reg_data1, base + WES_REF_DATA1_RED_OFF);
    writel(digitalData.reg_data2, base + WES_REF_DATA2_RED_OFF);

    /* Config WES_COMP_DATA0~2 EN */
    Wes_DigitalCfgToReg(&funcCtrl->digitalDataCompareEnable, &digitalData);
    writel(digitalData.reg_data0, base + WES_COMP_DATA0_EN_OFF);
    writel(digitalData.reg_data1, base + WES_COMP_DATA1_EN_OFF);
    writel(digitalData.reg_data2, base + WES_COMP_DATA2_EN_OFF);

    writel(digitalData.reg_data0, base + WES_COMP_DATA0_EN_RED_OFF);
    writel(digitalData.reg_data1, base + WES_COMP_DATA1_EN_RED_OFF);
    writel(digitalData.reg_data2, base + WES_COMP_DATA2_EN_RED_OFF);

    /* Config WES_STAB_VAL */
    regVal = readl(base + WES_STAB_VAL_OFF);

    regVal &= ~FM_WES_STAB_VAL_CLK_DIV;
    regVal |= FV_WES_STAB_VAL_CLK_DIV(funcCtrl->dividerForStabilization);

    regVal &= ~FM_WES_STAB_VAL_STAB_CNT_VAL;
    regVal |= FV_WES_STAB_VAL_STAB_CNT_VAL(funcCtrl->counterForStabilization);

    regVal &= ~FM_WES_STAB_VAL_SEL_CNT_VAL;
    regVal |=
        FV_WES_STAB_VAL_SEL_CNT_VAL(funcCtrl->counterForDselPortStabilization);

    writel(regVal, base + WES_STAB_VAL_OFF);
    writel(regVal, base + WES_STAB_VAL_RED_OFF);

    /* Config WES_ANA_CTRL */
    regVal = readl(base + WES_ANA_CTL_OFF);

    regVal &= ~FM_WES_ANA_CTL_SADC_TID_FW;
    regVal |= FV_WES_ANA_CTL_SADC_TID_FW(funcCtrl->analogSadcTid);

    writel(regVal, base + WES_ANA_CTL_OFF);
    writel(regVal, base + WES_ANA_CTL_RED_OFF);

    /* Config WES_ANA_TOUT_VAL */
    regVal = readl(base + WES_ANA_TOUT_CTL_OFF);

    regVal &= ~FM_WES_ANA_TOUT_CTL_TOUT_VAL;
    regVal |= FV_WES_ANA_TOUT_CTL_TOUT_VAL(funcCtrl->wdtTimeoutCounter);

    if (funcCtrl->analogWdtEnable) {
        regVal |= BM_WES_ANA_TOUT_CTL_WDT_EN;
    } else {
        regVal &= ~BM_WES_ANA_TOUT_CTL_WDT_EN;
    }

    writel(regVal, base + WES_ANA_TOUT_CTL_OFF);
    writel(regVal, base + WES_ANA_TOUT_CTL_RED_OFF);

    if (Wes_WaitBit(base + WES_FUNC_SET_DONE_OFF, 1U, 0U,
                    WES_POLLING_SET_DONE_MAX_TIMES)) {

        regVal = readl(base + WES_FUNC_SET_DONE_OFF);
        regVal |= BM_WES_FUNC_SET_DONE_SET_DONE;
        writel(regVal, base + WES_FUNC_SET_DONE_OFF);

        if (Wes_WaitBit(base + WES_FUNC_SET_DONE_OFF, 1U, 1U,
                        WES_POLLING_SET_DONE_MAX_TIMES)) {
            ret = WES_OK;
        } else {
            ret = WES_E_ERROR(WES_SET_UNDONE);
        }
    } else {
        ret = WES_E_ERROR(WES_SET_UNDONE);
    }

    return ret;
}

/**
 * @brief Fetches digital sample data from a specified base address and converts
 * it to a user-defined configuration format.
 *
 * This function reads digital sample data from three consecutive registers
 * starting from the given base address. The read data is then converted into a
 * user-defined configuration format using the helper function
 * Wes_DigitalRegToCfg.
 *
 * @param base The base address from which to read the digital sample data.
 * @param sampleData Pointer to a variable of type Wes_DigitalSamplePortType
 * that will hold the converted sample data.
 *
 * @return Returns WES_OK if the operation is successful.
 */
int32_t Wes_GetDigitalSampleData(uint32_t base,
                                 Wes_DigitalSamplePortType *sampleData)
{
    Wes_DigitalDataType digitalData;

    digitalData.reg_data0 = readl(base + WES_SAMP_DATA0_OFF);
    digitalData.reg_data1 = readl(base + WES_SAMP_DATA1_OFF);
    digitalData.reg_data2 = readl(base + WES_SAMP_DATA2_OFF);

    Wes_DigitalRegToCfg(&digitalData, sampleData);

    return WES_OK;
}

/**
 * @brief This function retrieves the analog SADC (Sigma-Delta Analog-to-Digital
 * Converter) conversion data from the specified base address.
 *
 * @param base        The base address of the WES (Wireless Energy Sensor) IP
 * block.
 * @param sampleData  Pointer to a structure of type Wes_AnalogSampleDataType
 * where the SADC conversion data and TID (Tag Identifier) will be stored.
 *
 * @return Returns WES_OK if the function successfully retrieves the conversion
 * data.
 *
 * @details The function reads the control register of the WES analog block at
 * the given base address plus the offset WES_ANA_CTL_OFF. It then extracts the
 *          SADC TID and conversion data from the read register value using
 * bitwise operations defined by GFV_WES_ANA_CTL_SADC_TID_BW and
 * GFV_WES_ANA_CTL_SADC_DATA macros, respectively. The extracted values are
 * stored in the provided sampleData structure.
 */
int32_t Wes_GetAnalogSadcConversionData(uint32_t base,
                                        Wes_AnalogSampleDataType *sampleData)
{
    uint32_t regVal;

    regVal = readl(base + WES_ANA_CTL_OFF);

    sampleData->sadcTid = GFV_WES_ANA_CTL_SADC_TID_BW(regVal);
    sampleData->sadcConversionData = GFV_WES_ANA_CTL_SADC_DATA(regVal);

    return WES_OK;
}

/**
 * @brief Set the control parameters for a timer.
 *
 * This function configures the timer based on the provided control parameters.
 * It sets the clock monitoring enable, clock enable, clock source, work mode,
 * clock divider, counter enable, compare interrupt enable, and overflow
 * interrupt enable for the specified timer.
 *
 * @param base        Base address of the timer module.
 * @param timer       The type of timer to configure.
 * @param timerCtrl   Pointer to a structure containing the timer control
 * parameters.
 *
 * @return Returns WES_OK if the timer control parameters were set successfully,
 *         otherwise returns an error code.
 */
int32_t Wes_SetTimerCtrl(uint32_t base, Wes_TimerType timer,
                         Wes_TimerCtrlType *timerCtrl)
{
    uint32_t timerComCtrlReg, timerComCtrlVal;
    uint32_t timerEventCtrl0Reg, timerEventCtrl0Val;
    uint32_t timerEventCtrl1Reg, timerEventCtrl1Val;
    uint32_t timerStaReg, timerStaVal;
    Wes_TimerEventType *timerEvent;
    uint32_t regVal;
    int32_t ret;

    timerComCtrlReg = base + WES_TIMER_COM_CTRL(timer);
    timerStaReg = base + WES_TIMER_STA(timer);

    timerComCtrlVal = readl(timerComCtrlReg);
    timerStaVal = readl(timerStaReg);

    if (timerCtrl->clkMonEn) {
        timerComCtrlVal |= BM_TIMER0_COM_CTRL_CLK_MON_EN;
    } else {
        timerComCtrlVal &= ~BM_TIMER0_COM_CTRL_CLK_MON_EN;
    }

    timerComCtrlVal |= BM_TIMER0_COM_CTRL_CLK_EN;

    timerComCtrlVal &= ~FM_TIMER0_COM_CTRL_CLK_SEL_1;
    timerComCtrlVal &= ~FM_TIMER0_COM_CTRL_CLK_SEL;
    timerComCtrlVal |= FV_TIMER0_COM_CTRL_CLK_SEL_1(timerCtrl->clkSource);
    timerComCtrlVal |= FV_TIMER0_COM_CTRL_CLK_SEL(timerCtrl->clkSource);

    if (timerCtrl->workMode == WES_NORMAL_MODE) {
        timerComCtrlVal &= ~BM_TIMER0_COM_CTRL_WORK_MODE;
    } else {
        timerComCtrlVal |= BM_TIMER0_COM_CTRL_WORK_MODE;
    }

    timerComCtrlVal &= ~FM_TIMER0_COM_CTRL_CLK_DIV_NUM;
    timerComCtrlVal |= FV_TIMER0_COM_CTRL_CLK_DIV_NUM(timerCtrl->clkDivider);

    timerComCtrlVal &= ~FM_TIMER0_COM_CTRL_TIMER_CNT_EN;

    timerStaVal &= ~(0xFFU << 0U);

    for (uint32_t i = 0U; i < WES_TIMER_COUNTER_MAX; i++) {
        timerEvent = &timerCtrl->counterEvent[i];
        timerEventCtrl0Reg = base + WES_TIMER_EVENT_CTRL0(timer, i);
        timerEventCtrl1Reg = base + WES_TIMER_EVENT_CTRL1(timer, i);

        timerEventCtrl0Val =
            FV_TIMER0_EVENT0_CTRL0_OVF_VAL(timerEvent->overflowValue) |
            FV_TIMER0_EVENT0_CTRL0_COMP_VAL(timerEvent->compareValue);

        timerEventCtrl1Val = readl(timerEventCtrl1Reg);

        if (timerEvent->pwmPolar) {
            timerEventCtrl1Val |= BM_TIMER0_EVENT0_CTRL1_PWM_POLAR;
        } else {
            timerEventCtrl1Val &= ~BM_TIMER0_EVENT0_CTRL1_PWM_POLAR;
        }

        if (timerEvent->pwmDefault) {
            timerEventCtrl1Val |= BM_TIMER0_EVENT0_CTRL1_DEF_VAL;
        } else {
            timerEventCtrl1Val &= ~BM_TIMER0_EVENT0_CTRL1_DEF_VAL;
        }

        writel(timerEventCtrl0Val, timerEventCtrl0Reg);
        writel(timerEventCtrl1Val, timerEventCtrl1Reg);

        if (timerEvent->counterEnable) {
            timerComCtrlVal |= (0x1U << (4U + i));
        }

        if (timerEvent->compareIntEnable) {
            timerStaVal |= (0x1U << (0U + i));
        }

        if (timerEvent->overflowIntEnable) {
            timerStaVal |= (0x1U << (4U + i));
        }
    }

    writel(timerComCtrlVal, timerComCtrlReg);
    writel(timerStaVal, timerStaReg);

    if (Wes_WaitBit(base + WES_TIMER_SET_DONE(timer), 1U, 0U,
                    WES_POLLING_SET_DONE_MAX_TIMES)) {

        regVal = readl(base + WES_TIMER_SET_DONE(timer));
        regVal |= BM_WES_TIMER0_SET_DONE_SET_DONE;
        writel(regVal, base + WES_TIMER_SET_DONE(timer));

        if (Wes_WaitBit(base + WES_TIMER_SET_DONE(timer), 1U, 1U,
                        WES_POLLING_SET_DONE_MAX_TIMES)) {
            ret = WES_OK;
        } else {
            ret = WES_E_ERROR(WES_SET_UNDONE);
        }
    } else {
        ret = WES_E_ERROR(WES_SET_UNDONE);
    }

    return ret;
}

/**
 * @brief Get the interrupt status of a specific timer.
 *
 * This function retrieves the interrupt status of a specified timer by reading
 * the corresponding status register. The interrupt status is masked to extract
 * only the relevant bits from the register.
 *
 * @param base The base address of the timer module.
 * @param timer The type of timer to check the interrupt status for. This should
 * be of type Wes_TimerType, which enumerates the different timers.
 *
 * @return A 32-bit value representing the interrupt status of the specified
 * timer. The returned value contains only the relevant bits from the status
 *         register, specifically the bits located between 16 and 23
 * (inclusive).
 */
uint32_t Wes_GetTimerInterruptStatus(uint32_t base, Wes_TimerType timer)
{
    uint32_t timerStaReg, timerStaVal;

    timerStaReg = base + WES_TIMER_STA(timer);
    timerStaVal = readl(timerStaReg);

    return timerStaVal & (0xFFU << 16U);
}

/**
 * @brief Clears the interrupt status for a specified timer.
 *
 * This function reads the interrupt status register for the given timer,
 * effectively clearing the interrupt by writing back the same value.
 *
 * @param base The base address of the timer module.
 * @param timer The type of timer to clear the interrupt status for.
 *              This should be of type Wes_TimerType, which is an enumeration
 *              representing the different timers available.
 *
 * @return WES_OK upon successful completion of the operation.
 */
int32_t Wes_ClearTimerInterruptStatus(uint32_t base, Wes_TimerType timer)
{
    uint32_t timerStaReg, timerStaVal;

    timerStaReg = base + WES_TIMER_STA(timer);
    timerStaVal = readl(timerStaReg);
    writel(timerStaVal, timerStaReg);

    return WES_OK;
}

/**
 * @brief Get the FUSA interrupt status from the specified base address.
 *
 * This function reads the interrupt status register of the FUSA
 * module, which is located at an offset from the given base address.
 * It then masks the upper 16 bits of the register value and returns this masked
 * value.
 *
 * @param base The base address of the FUSA module.
 * @return The masked interrupt status (upper 16 bits of the interrupt status
 * register).
 */
uint32_t Wes_GetFusaInterruptStatus(uint32_t base)
{
    uint32_t regVal;

    regVal = readl(base + WES_FUSA_INT_OFF);

    return regVal & (0xFFFFU << 16U);
}

/**
 * @brief Clears the FUSA interrupt status.
 *
 * This function reads the interrupt status register of the FUSA
 * module, effectively acknowledging and clearing the current interrupt status.
 *
 * @param base The base address of the FUSA module's register space.
 *
 * @return Always returns WES_OK indicating success.
 */
int32_t Wes_ClearFusaInterruptStatus(uint32_t base)
{
    uint32_t regVal;

    regVal = readl(base + WES_FUSA_INT_OFF);
    writel(regVal, base + WES_FUSA_INT_OFF);

    return WES_OK;
}

/**
 * @brief       Gets the loopback check error status.
 *
 * @param       base  The base address of the Wes IP block.
 *
 * @return      Returns the loopback check error status.
 *              The status is read from the register at (base + LP_CHECK_OFF).
 */
uint32_t Wes_GetLoopbackCheckErrorStatus(uint32_t base)
{
    return readl(base + LP_CHECK_OFF);
}

/**
 * @brief Clears the loopback check error status bits in the specified register.
 *
 * This function reads the loopback check status register, clears the specified
 * error status bits, and writes the updated value back to the register.
 *
 * @param base The base address of the hardware register block.
 * @return WES_OK upon successful completion of the operation.
 */
int32_t Wes_ClearLoopbackCheckErrorStatus(uint32_t base)
{
    uint32_t regVal;

    regVal = readl(base + LP_CHECK_OFF);

    regVal &= ~FM_LP_CHECK_ASE_ST;
    regVal &= ~FM_LP_CHECK_DSE_ST;
    regVal &= ~FM_LP_CHECK_DSEL_ST;
    regVal &= ~FM_LP_CHECK_SELFTEST_ST;
    regVal &= ~FM_LP_CHECK_PWM_ST;

    writel(regVal, base + LP_CHECK_OFF);

    return WES_OK;
}

/**
 * @brief Set the FUSA interrupt configuration.
 *
 * This function reads the current interrupt configuration from the specified
 * base address, clears the relevant interrupt enable bits, and then sets the
 * bits according to the provided configuration structure. The updated
 * configuration is then written back to the hardware registers.
 *
 * @param base The base address of the WES registers.
 * @param fusaInt Pointer to a structure containing the interrupt configuration.
 *
 * @return Returns WES_OK if the configuration is set successfully.
 */
int32_t Wes_SetFusaInterrupt(uint32_t base,
                             Wes_FusaInterruptConfigType *fusaInt)
{
    uint32_t fusaIntVal, fusaInt1Val;

    fusaIntVal = readl(base + WES_FUSA_INT_OFF);
    fusaInt1Val = readl(base + WES_FUSA_INT_1_OFF);

    fusaIntVal &= ~(0xFFFFU << 0U);
    fusaInt1Val &= ~(0x3U << 0U);

    if (fusaInt->funcCfgIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_FUNC_CFG_INT_EN;
    }

    if (fusaInt->funcClkIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_FUNC_CLK_INT_EN;
    }

    if (fusaInt->funcCompIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_FUNC_COMP_INT_EN;
    }

    if (fusaInt->funcDseLpIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_FUNC_DSE_LP_INT_EN;
    }

    if (fusaInt->funcDselLpIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_FUNC_DSEL_LP_INT_EN;
    }

    if (fusaInt->funcAseLpIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_FUNC_ASE_LP_INT_EN;
    }

    if (fusaInt->timer0Pwm01IntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_TIMER0_PWM01_INT_EN;
    }

    if (fusaInt->timer0Pwm23IntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_TIMER0_PWM23_INT_EN;
    }

    if (fusaInt->timer1Pwm01IntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_TIMER1_PWM01_INT_EN;
    }

    if (fusaInt->timer1Pwm23IntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_TIMER1_PWM23_INT_EN;
    }

    if (fusaInt->timer0ClkIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_TIMER0_CLK_INT_EN;
    }

    if (fusaInt->timer1ClkIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_TIMER1_CLK_INT_EN;
    }

    if (fusaInt->testModeIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_TEST_MODE_INT_EN;
    }

    if (fusaInt->selfTestModeIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_SELFTEST_MODE_INT_EN;
    }

    if (fusaInt->wdtToutIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_WDT_TOUT_INT_EN;
    }

    if (fusaInt->lockstepIntEn) {
        fusaIntVal |= BM_WES_FUSA_INT_LKSTEP_INT_EN;
    }

    if (fusaInt->externTrgIntEn) {
        fusaInt1Val |= BM_WES_FUSA_INT_1_EXT_TRG_INT_EN;
    }

    if (fusaInt->internTrgIntEn) {
        fusaInt1Val |= BM_WES_FUSA_INT_1_INTER_TRG_INT_EN;
    }

    writel(fusaIntVal, base + WES_FUSA_INT_OFF);
    writel(fusaInt1Val, base + WES_FUSA_INT_1_OFF);

    return WES_OK;
}

/**
 * @brief Set APB Error interrupt enable.
 *
 * @param base The base address of the WES registers.
 * @param intType Each bit represent one interrupt type, defined in
 * Wes_ApbErrInterruptEnableType.
 * @param enable Interrupt enable or not.
 *
 * @return Returns WES_OK if the configuration is set successfully.
 */
int32_t Wes_ApbErrorInterruptEnable(uint32_t base, uint32_t intType,
                                    bool enable)
{
    uint32_t val = readl(base + APB_ERR_INT_OFF(0));

    if (enable) {
        val |= intType;
    }
    else {
        val &= ~intType;
    }

    writel(val, base + APB_ERR_INT_OFF(0));

    return WES_OK;
}

/**
 * @brief Clear APB Error interrupt status.
 *
 * @param base The base address of the WES registers.
 * @param intType Each bit represent one interrupt status, defined in
 * Wes_ApbErrInterruptStatusType.
 *
 * @return Returns WES_OK if the configuration is set successfully.
 */
int32_t Wes_ClearApbErrorInterruptStatus(uint32_t base, uint32_t statusType)
{
    uint32_t val = readl(base + APB_ERR_INT_OFF(0));

    val |= statusType;

    writel(val, base + APB_ERR_INT_OFF(0));

    return WES_OK;
}

/* End of file */
